/*
  crcany version 2.1, 19 September 2021

  Copyright (C) 2014, 2016, 2017, 2020, 2021 Mark Adler

  This software is provided 'as-is', without any express or implied warranty.
  In no event will the authors be held liable for any damages arising from the
  use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not claim
     that you wrote the original software. If you use this software in a
     product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Mark Adler
  madler@alumni.caltech.edu
 */

/* Usage:

    crcany [-pattern] [path1] [path2] ...

 where pattern is what to search for in the list of CRC names, and the paths
 are the inputs to compute the CRCs on. If there are no paths, then input is
 taken from stdin. If there is no pattern supplied, then "-32" is assumed,
 which computes all 32-bit CRCs on the input(s).

 If the pattern is "-help" or "-list", then the names of the available CRCs are
 listed, and any input is ignored.

 If the pattern starts with "CRC" or "crc", then that is removed. Any non-
 alphanumeric characters are removed from the pattern. If the remaining pattern
 starts with digits, then that is the width of the CRC, where only CRCs of that
 width will match. (I.e. "3" does not match "32".) The remainder of the pattern
 after any digits must appear somewhere in the CRC name for a match. All
 matching is case insensitive. The empty pattern, option "-", will match all
 the available CRCs.

 */

/* Version history:
   1.0  22 Dec 2014  First version
   1.1  15 Jul 2016  Allow negative numbers
                     Move common code to model.[ch]
   1.2  17 Jul 2016  Move generic CRC code to crc.[ch] and crcdbl.[ch]
   1.3  23 Jul 2016  Build xorout into the tables
   1.4  30 Jul 2016  Fix a bug in word-wise table generation
                     Reduce verbosity of testing
   1.5  23 Oct 2016  Improve use of data types and C99 compatibility
                     Add verifications summary message
   1.6  11 Feb 2017  Add creation of remaining bits function (_rem)
                     Add and check the residue value of a model
                     Improve the generated comments and prototypes
   1.7  23 Dec 2017  Update to the latest CRC catalog
                     Minor improvements to code generation
   2.0  29 Dec 2020  Use fixed-width integers in generated code
                     Normalize generated-code loop constructs
                     Optimize the generated bit-reversal code
                     Refactor to split tests from code generation
                     Add crcadd to generate CRC code without testing
                     Bring code into compliance with C99 standard
                     Replace Ruby script with Python for portability
                     Add copy of Greg Cook's all-CRCs page for safekeeping
                     Update to the latest CRC catalog
   2.1  19 Sep 2021  Generate code for combining CRCs
                     Add options to crcadd for endianess and word size
                     Split off checks of CRC lists to a checklists make target
                     Enhance crcany to compute multiple CRCs on the same input
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>

#define local static

// Include all CRC functions referenced by a table of functions all[], and
// define a CRC function type crc_f. src/allcrcs.c is generated by crcgen. This
// needs to be linked with compilations of src/crc*.c, also generated by
// crcgen.
#include "src/allcrcs.c"

// Normalize str in place to contain only lower case letters and digits.
local void normalize(char *str) {
    char *p = str;
    while (islower(*p) || isdigit(*p))
        p++;
    while (isalnum(*p)) {
        if (isupper(*p))
            *p += 'a' - 'A';
        p++;
    }
    if (*p) {
        char *q = p;
        while (*++p)
            if (isalnum(*p))
                *q++ = tolower(*p);
        *q = 0;
    }
}

// Return the number of leading digits in str.
local size_t digs(char const *str) {
    char const *p = str;
    while (isdigit(*p))
        p++;
    return p - str;
}

// Number of CRCs defined in all (last entry is zeros).
#define NUMALL (sizeof(all) / sizeof(all[0]) - 1)

// Find all CRC names from the list that start with id. hit[] must have space
// for NUMALL entries. On return hit[i] is true for a match to id. The return
// value is the number of matches.
local int matches(char *id, char *hit) {
    // Default CRC = all 32-bit CRCs.
    char def[] = "32";              // mutable string
    if (id == NULL)
        id = def;

    // Normalize the id to lower case letters and digits.
    normalize(id);

    // If asking for help or a list, list all of the CRCs. Return -1.
    if (strcmp(id, "help") == 0 || strcmp(id, "list") == 0) {
        for (size_t k = 0; k < NUMALL; k++)
            printf("%s (%s)\n", all[k].name, all[k].match);
        return -1;
    }

    // Drop the leading "crc", if present.
    if (strncmp(id, "crc", 3) == 0)
        id += 3;

    // Search for a matching CRC.
    memset(hit, 0, NUMALL);
    size_t di = digs(id);
    int any = 0;
    for (size_t k = 0; k < NUMALL; k++) {
        char const *match = all[k].match;
        size_t dm = digs(match);
        // If id starts with digits, then match must start with those same
        // digits, i.e., the CRC widths much match. Then the portion of id
        // after any initial digits must be found somewhere in match after its
        // initial digits.
        if ((di == 0 || (di == dm && memcmp(id, match, di) == 0)) &&
            strstr(match + dm, id + di) != NULL) {
            hit[k] = 1;
            any++;
        }
    }
    return any;
}

// Compute the CRCs of the file in, using the CRC's marked in hit. The CRCs are
// returned in crc[], which must have room for NUMALL entries.
local void crcs_file(uintmax_t *crc, char *hit, FILE *in) {
    unsigned char buf[16384];
    size_t got;
    for (size_t k = 0; k < NUMALL; k++)
        if (hit[k])
            crc[k] = all[k].func(0, NULL, 0);
    while ((got = fread(buf, 1, sizeof(buf), in)) != 0)
        for (size_t k = 0; k < NUMALL; k++)
            if (hit[k])
                crc[k] = all[k].func(crc[k], buf, got);
}

// Print the specified CRCs computed on the provided files or on stdin.
int main(int argc, char **argv) {
    // Get the CRCs to apply.
    char hit[NUMALL];
    int n = 1;
    int m = matches(n < argc && argv[n][0] == '-' ? argv[n++] + 1 : NULL, hit);
    if (m < 0)
        return 0;
    if (m == 0) {
        fputs("no matches\n", stderr);
        return 1;
    }

    // Compute the CRC of the paths in the remaining arguments, or of stdin if
    // there are no more arguments. Include the paths in the output if there
    // are two or more paths.
    int ret = 0;                    // set to 1 if any file errors
    int name = argc - n > 1;        // true to print the path name with the CRC
    do {
        // Open the input.
        FILE *in = n == argc ? stdin : fopen(argv[n], "rb");
        if (in == NULL) {           // open error
            perror(argv[n]);
            ret = 1;
            continue;
        }

        // Compute the CRCs on the input.
        uintmax_t crc[NUMALL];
        crcs_file(crc, hit, in);
        if (ferror(in)) {           // read error
            perror(n == argc ? NULL : argv[n]);
            ret = 1;
        }
        else {
            // Show the computed CRCs.
            if (name)
                printf("%s:\n", argv[n]);
            for (size_t k = 0; k < NUMALL; k++) {
                if (hit[k])
                    printf("%s%s: 0x%0*jx\n", name ? "  " : "",
                           all[k].name, (all[k].width + 3) >> 2, crc[k]);
            }
        }

        // Close the input.
        if (in != stdin)
            fclose(in);
    } while (++n < argc);
    return ret;
}
